#include "MeshGrid.h"
#include "Memory.h"
#include "Triangle.h"
#include "File.h"
#include "Game.h"
#include <sstream>
#include "Texture.h"
#include <assert.h>
#include "Debug.h"
#include "GameState.h"
/************************************************************************/
/* MESH IMPLEMENTATION                                                  */
/************************************************************************/
void MeshGrid::CleanUp()
{
    if(m_texture)
        m_texture = NULL;
    if(m_renderables.size() > 10)
    {
        int halfWay = m_renderables.size() / 2;
        std::cout <<"Grid Clean up 0%...\n";
        for (unsigned int index = 0; index < m_renderables.size(); ++index) 
        {
            if(index == halfWay)
                std::cout <<"Grid Clean up 50%...\n";
            delete m_renderables[index];
            m_renderables[index] = NULL;
        }	
        std::cout <<"Grid Clean up 100%...\n";
    }
    else
    {
        for (unsigned int index = 0; index < m_renderables.size(); ++index) 
        {
            delete m_renderables[index];
            m_renderables[index] = NULL;
        }	
    }
    m_renderables.erase (m_renderables.begin(), m_renderables.end());

    if(m_textures.size() > 10)
    {
        int halfWay = m_textures.size() / 2;
        std::cout <<"Texture Clean up 0%...\n";
        for(unsigned int index = 0; index < m_textures.size(); ++ index)
        {
           if(index == halfWay)
               std::cout <<"Texture Clean up 50%...\n";
            m_textures[index]->CleanUp();
            delete m_textures[index];
        }
        std::cout <<"Texture Clean up 100%...\n";
    }
    else
    {
        for(unsigned int index = 0; index < m_textures.size(); ++ index)
        {
            m_textures[index]->CleanUp();
            delete m_textures[index];
        }
    }
    m_textures.clear();
    m_grid.CleanUp();
}
bool MeshGrid::CreateFromOBJ( const char* fileName,const Vec3& scale )
{
    std::cout << "Mesh loading...\n";
    File file;
    if(!file.OpenRead(std::string(fileName)))
        return false;
    std::string s;
    while(file.GetString(&s))
    {
        if ( s[0] == 'v' && s[1] == 't')
        {
            //this is a uv
            CreateUv(s);
        }
        else if ( s[0] == 'v' && s[1] == 'n')
        {
            //this is a vertext normal
            CreateNormal(s);
        }
        else if( s[0] == 'v')
        {
            //this is a vert
            CreateVertex(s);
            m_totalVerts++;
        }
        else if ( s[0] == 'f')
        {
            //this is a triangle
            CreateTri(s);
            m_triangleCount++;
        }
        else if( s[0] == 'm')
        {
            //we got ourselvers a material file
            bool failed = LoadInMaterials(s);
            //assert(failed);
        }
        else if( s[0] == 'u')
        {
            //we have a usemtl call with the name of the material that
            //the follow F calls will need to know about
            AssignCurrentMaterial(s);
        }
    }
    for(unsigned int i = 0; i < m_verts.size(); ++i)
    {
        m_verts[i].Scale(scale);
    }
    std::cout << "Mesh loaded!...\n";
    return true;
}
void MeshGrid::GenerateGrid(bool sShading, bool textured)
{
    m_renderables.reserve(m_triangleCount);
    int halfWay = m_triangles.size() / 2;
    std::cout << "Grid Generation 0%...\n";
    for(unsigned int i = 0; i < m_triangles.size(); ++i)
    {
        if(i == halfWay)
            std::cout << "Grid Generation 50%...\n";
        //store the data here
        Vec3 v[3];
        v[0] = m_verts[m_triangles[i].m_vertIndex[0]];
        v[1] = m_verts[m_triangles[i].m_vertIndex[1]];
        v[2] = m_verts[m_triangles[i].m_vertIndex[2]];
        //for each triangle, make a Renderable triangle, and add it to renderables
        Triangle* t = new Triangle(v[0],v[1], v[2], m_material,m_reflective, 
                                    m_reflectance, m_transparent, m_opacity);
        if(sShading)
            t->SetSmootheShadingNormals(m_normals[m_triangles[i].m_normalIndex[0]],
                                        m_normals[m_triangles[i].m_normalIndex[1]],
                                        m_normals[m_triangles[i].m_normalIndex[2]]);
        else
            t->CalculateNormal();
        if(textured)
        {
            if(m_triangles[i].m_materialIndex == -1)
                t->SetTexture(m_texture);
            else
            {
                int texI = m_materials[m_triangles[i].m_materialIndex].textureLookup;
                t->SetTexture(m_textures[texI]);
            }
            t->SetUVs(  m_uvs[m_triangles[i].m_uvIndex[0]],
                        m_uvs[m_triangles[i].m_uvIndex[1]],
                        m_uvs[m_triangles[i].m_uvIndex[2]]);
        }
        t->SetColor(m_color);
        m_renderables.push_back(t);
    }
    m_grid.SetUpCells();
    m_box = m_grid.GetBox();
    std::cout << "Grid Generation 100%...\n";
#ifdef DEBUGGING
    m_information += "Class MeshGrid: \n";
#endif
}
bool MeshGrid::Hit( const Ray& ray, float& tmin, ContactData& data ) const
{
    return m_grid.Hit(ray, tmin, data);
}
/************************************************************************/
/* HELPER FUNCTION IMPLEMENTATION                                       */
/************************************************************************/
void MeshGrid::CreateVertex( const std::string& s )
{
    /*comes in "v 0.000000 0.000000 0.000000"
                or "v -0.000000 -0.000000 -0.000000*/
    
    //remove the "v "
    std::string store;
    int i = 0;
    while(s[i] != ' ')
        ++i;
    if(s[i+1] == ' ')
        ++i;
    store = s.substr(i+1);
    
    //variables
    Vec3 temp;
    Create(&store, &temp, 3);
    
    //have the vector, store it,
    m_verts.push_back(temp);
}
void MeshGrid::CreateNormal( const std::string& s )
{
    /* comes in "vn 0.000000 0.000000 0.00000
                 or "vn -0.000000 -0.000000 -0.000000 */
    
    //remove the "vn "
    std::string store = s.substr(3);
    //variables
    Vec3 temp;
    Create(&store, &temp, 3);
    //have the vector, store it,
    m_normals.push_back(temp);
}
void MeshGrid::CreateUv( const std::string& s )
{
    /*comes in "vt 0.000000 0.000000 " */
    //remove the "vt "
    std::string store = s.substr(3);
    Vec3 temp;
    Create(&store, &temp, 2);
    //temp.x = 1- temp.x;
    //temp.m_y = 1- temp.m_y;
    //have the vector, store it,
    if(temp.m_x > 1 || temp.m_x < 0)
        temp.m_x = temp.m_x - floor(temp.m_x / 1) * 1;
    if(temp.m_y > 1 || temp.m_y < 0)
        temp.m_y = temp.m_y - floor(temp.m_y / 1) * 1;
    m_uvs.push_back(temp);
}
void MeshGrid::CreateTri( const std::string& s )
{
    /* comes in "f 0/0/0 0/0/0 0/0/0" 
                 or "f  0/0/0 0/0/0 0/0/0"
            f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 */
    
    //remove the "f "
    int removal = 0;
    if(s[2] != ' ' )
    {
        removal = 2;
    }
    else
    {
        removal = 3;
    }
    std::string store = s.substr(removal);
        
    IndexedTriangle tri;
    for( int i = 0; i < 3; ++i)
    {
        int ai[3] = {0,0,0};
        GetInts(ai, store);
        if( i < 2)
        {
            //1 space, 2 slashes, plus number of characters.
            std::ostringstream oss;
            oss << ai[0] << ai[1] << ai[2];
            std::string size = oss.str();
            store = store.substr(size.size() + 3);
        }
        tri.m_vertIndex[i] = ai[0] -1;
        tri.m_uvIndex[i] = ai[1] -1;
        tri.m_normalIndex[i] = ai[2] -1;
    }
    if(m_currentMaterial.size() > 0)
    {
        unsigned int index = -1;
        unsigned int i = 0;
        while(i < m_materials.size())
        {
            if(m_materials[i].name.size() == m_currentMaterial.size() &&
               m_materials[i].name == m_currentMaterial)
            {
                index = i;
                break;//found our index
            }
            ++i;
        }
        if(index != -1)
            tri.m_materialIndex = index;
    }
    m_triangles.push_back(tri);
}
void MeshGrid::Create(std::string* store, Vec3* v3, int num)
{
    //makes sure we itterate the correct number of times
    int index = 0;
    std::string target;
    while(index < num)
    {
        //gets the float from the string
        GetFloat(*store,&target);
        //store the float in the vector
        (*v3)[index] = StoF(target);
        //increment the index
        index++;
        if(index < num)
        {
            //remove the m_data from the store string
            *store = (*store).substr(target.size()+1);
        }
    }
}
void MeshGrid::GetFloat(const std::string& source, std::string* target)
{
    int index = 0;
    for(unsigned int i = 0; i < source.size(); ++i)
    {
        if(source[i] == ' ' || source[i] == '/')
        {
            break;
        }
        index++;
    }
    *target = source.substr(0,index);
}
float MeshGrid::StoF(const std::string& s)
{
    return (float)atof(s.c_str());
}
int MeshGrid::StoI(const std::string& s)
{
    return atoi(s.c_str());
}
void MeshGrid::GetInts(int* i, std::string s)
{
    //calculate length of first number
    int count = 0;
    std::string temp;
    //designed to work with arrays of 3 ints!!
    while(count < 3)
    {
        unsigned int index = 0;
        for(; index < s.size(); ++index)
        {
            if(s[index] == '/' || s[index] == ' ')
            {
                break;
            }
        }
        temp = s.substr(0,index);
        *i = StoI(temp);
        count++;
        if(count < 3)
        {
            //chop it off!
            s = s.substr(index+1);
        }
        ++i;
    }
}
void MeshGrid::ReverseNormals()
{
    //EDIT the mesh data
    for(unsigned int i = 0; i < m_normals.size(); ++i)
    {
        m_normals[i] *= -1;
    }
}
void MeshGrid::CalculateBox()
{
    //we use grids box
    m_hasValidBox = true;
}
bool MeshGrid::LoadInMaterials( const std::string& fileName )
{
    //comes in like this: mtllib city.mtl
    File file;
    //remove the mtllib bit
    std::string store;
    int i = 0;
    while(fileName[i] != ' ')
        ++i;
    if(fileName[i+1] == ' ')
        ++i;
    store = fileName.substr(i+1);

    if(!file.OpenRead(std::string("Meshes/") + store))
        return false;

    std::string s;
    while(file.GetString(&s))
    {
      /* newmtl 82.Mat0Tex0_roofti5.jpg
         map_Kd roofti5.jpg
      */
        if ( s[0] == 'n' )
        {
            //this is a new material
            CreateMaterial(s,file);
        }
    }
    return true;
}
void MeshGrid::AssignCurrentMaterial(const std::string& s)
{
   //comes in usemtl 82.Mat0Tex0_roofti5.jpg formation
    //cut off the usemtl
    std::string store;
    int i = 0;
    while(s[i] != ' ')
        ++i;
    if(s[i+1] == ' ')
        ++i;
    store = s.substr(i+1);
    m_currentMaterial = store;
}
void MeshGrid::CreateMaterial( const std::string& s, File& file )
{
    obj_material m;
    //name comes in: newmtl 82.Mat0Tex0_roofti5.jpg form
    //get rit of newmtl bit
    std::string store;
    int i = 0;
    while(s[i] != ' ')
        ++i;
    if(s[i+1] == ' ')
        ++i;
    store = s.substr(i+1);
    m.name = store;    store.clear();
    while(file.GetString(&store))
    {
        if(store[0] == 'n')//new material, run away
            return;
        else if(store[0] == 'm')//map_Kd yard_60.jpg
        {
            std::string texName;
            int j = 0;
            while(store[j] != ' ')
                ++j;
            if(store[j+1] == ' ')
                ++j;
            texName = store.substr(j+1);
            Texture* tex = new Texture();
            std::string tn = "Textures/" + texName;
            if(!tex->LoadFromFile(tn.c_str()))
                tex->LoadFromFile("Textures/default.png");
            m.textureLookup = m_textures.size();
            m_textures.push_back(tex);
            m_materials.push_back(m);
            return;
        }

    }
}
#ifdef DEBUGGING
bool MeshGrid::Hit_Debug( const Ray& ray, float& tmin, ContactData& data ) const
{
    bool ret = m_grid.Hit_Debug(ray, tmin, data);
    if(ret)
    {
        OUT_STRING("Hit_Debug on MeshGrid class returned true \n");
        return ret;
    }
    else
    {
        OUT_STRING("Hit_Debug on MeshGrid class returned false\n");
        return ret;
    }
}
#endif

